package com.coffee.admin.aop;

import com.alibaba.fastjson.JSONObject;
import com.coffee.admin.controller.BaseController;
import com.coffee.core.anotation.OperLog;
import com.coffee.core.entity.LoginUser;
import com.coffee.core.entity.OperationLog;
import com.coffee.core.exception.BusinessException;
import com.coffee.core.util.DateUtil;
import com.coffee.core.util.IpUtils;
import org.apache.dubbo.common.utils.IOUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;


@Aspect
@Component
public class OperLogAspect extends BaseController {

    private static final Logger logger = LoggerFactory.getLogger(OperLogAspect.class);


    @Value("${dubbo.application.name}")
    public String operService;

    @Value("${spring.profiles.active}")
    public String operEnvironment;

    @Value("${logstash.address:null}")
    private String addressArray;

    /**
     * 设置操作日志切入点 记录操作日志 在注解的位置切入代码
     */
    @Pointcut("@annotation(com.coffee.core.anotation.OperLog)")
    public void operLogPoinCut() {
    }

    /**
     * 设置操作异常切入点记录异常日志 扫描所有controller包下操作
     */
    @Pointcut("execution(* com.coffee.admin.interceptor.controller..*.*(..))")
    public void operExceptionLogPoinCut() {
    }

    /**
     * 正常返回通知，拦截用户操作日志，连接点正常执行完成后执行， 如果连接点抛出异常，则不会执行
     *
     * @param joinPoint 切入点
     * @param keys      返回结果
     */
    @AfterReturning(value = "operLogPoinCut()", returning = "keys")
    public void saveOperLog(JoinPoint joinPoint, Object keys) {
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);

        //设置OperationLog值
        OperationLog operlog = setOperationLog(request, joinPoint);
        operlog.setRespParam(JSONObject.toJSONString(keys)); // 返回结果

        //保存打印日志
        saveLog(operlog);
    }

    /**
     * 异常返回通知，用于拦截异常日志信息 连接点抛出异常后执行
     *
     * @param joinPoint 切入点
     * @param e         异常信息
     */
    @AfterThrowing(pointcut = "operExceptionLogPoinCut()", throwing = "e")
    public void saveExceptionLog(JoinPoint joinPoint, Throwable e) {
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);

        OperationLog operlog = setOperationLog(request, joinPoint);
        operlog.setExcName(e.getClass().getName()); // 异常名称
        logger.info("eee" + e);
        logger.info("eee" + e.getMessage());
        if (e != null) {
            if (e instanceof BusinessException) {
                operlog.setExcMessage(((BusinessException) e).getCode() + ":" + e.getMessage());
            } else {
                operlog.setExcMessage(e.getMessage());
            }
            //保存打印日志
            saveLog(operlog);

        }

    }


    public OperationLog setOperationLog(HttpServletRequest request, JoinPoint joinPoint) {
        OperationLog operlog = new OperationLog();
        // 从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取切入点所在的方法
        Method method = signature.getMethod();
        // 获取操作
        OperLog opLog = method.getAnnotation(OperLog.class);
        if (opLog != null) {
            String operModul = opLog.operModul();
            String operDesc = opLog.operDesc();
            operlog.setOperModul(operModul); // 操作模块
            operlog.setOperDesc(operDesc); // 操作描述
        }
        // 获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();
        // 获取请求的方法名
        String methodName = method.getName();
        methodName = className + "." + methodName;

        operlog.setOperMethod(methodName); // 请求方法

        // 请求的参数
        String operRequParam = converMap(request.getParameterMap());

        // 请求的body参数
        try {
            BufferedReader reader = request.getReader();
            String bodyStr = IOUtils.read(reader);
            operlog.setReqBody(bodyStr);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //获取请求方式
        String requestMethod = request.getMethod();
        operlog.setReqMethod(requestMethod);
        operlog.setReqParam(operRequParam); // 请求参数

        //操作员信息
        LoginUser currLoginUser = getSupplier();
        if (currLoginUser != null) {
            Integer integer = currLoginUser.getPlatformId() == null ? currLoginUser.getSupplierId() : currLoginUser.getPlatformId();
            operlog.setOperUserId(integer); // 操作员ID
            operlog.setOperUserName(currLoginUser.getUserName()); // 操作员名称
        }
        operlog.setOperIp(IpUtils.getIp(request)); // 请求IP
        operlog.setReqUrl(request.getRequestURI()); // 请求URI
        operlog.setOperTime(DateUtil.getDetailDateTime(new Date()));
        operlog.setOperEnvironment(operEnvironment);
        operlog.setOperService(operService);
        return operlog;
    }


    /**
     * 转换request 请求参数
     *
     * @param paramMap request获取的参数数组
     */
    public String converMap(Map<String, String[]> paramMap) {
        if (paramMap == null || paramMap.isEmpty()) {
            return "";
        }

        Map<String, String> rtnMap = new HashMap<String, String>();
        for (String key : paramMap.keySet()) {
            rtnMap.put(key, paramMap.get(key)[0]);
        }
        // 将参数所在的数组转换成json
        String params = JSONObject.toJSONString(rtnMap);
        return params;
    }


    /**
     * 转换异常信息为字符串
     *
     * @param exceptionName    异常名称
     * @param exceptionMessage 异常信息
     * @param elements         堆栈信息
     */
    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
        StringBuffer strbuff = new StringBuffer();
        for (StackTraceElement stet : elements) {
            strbuff.append(stet + "\n");
        }
        String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
        return message;
    }


    //保存日志到es中
    public void saveLog(OperationLog operlog) {
        String source = JSONObject.toJSONString(operlog);
        logger.info(source);

        /*if(!"null".equals(addressArray)){
            IndexRequest indexRequest = new IndexRequest("admin_log");
            indexRequest.source(source, XContentType.JSON);
            try {
                restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
                // baseElasticService.saveOperatorLog(operlog);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }*/
    }

}